# RelationsMetadataResolver.java

    package org.codefilarete.stalactite.engine.configurer.dslresolver;

    import java.util.ArrayDeque;
    import java.util.Deque;

    import org.codefilarete.stalactite.engine.configurer.dslresolver.MetadataSolvingCache.EntitySource;
    import org.codefilarete.stalactite.sql.ConnectionConfiguration;
    import org.codefilarete.stalactite.sql.Dialect;
    import org.codefilarete.tool.collection.KeepOrderSet;

    public class RelationsMetadataResolver {

    	private final OneToOneMetadataResolver oneToOneMetadataResolver;
    	private final OneToManyMetadataResolver oneToManyMetadataResolver;
    	private final ManyToManyMetadataResolver manyToManyMetadataResolver;
    	private final ManyToOneMetadataResolver manyToOneMetadataResolver;
    	private final MapMetadataResolver mapMetadataResolver;
    	private final ElementCollectionMetadataResolver elementCollectionMetadataResolver;

    	public RelationsMetadataResolver(Dialect dialect, ConnectionConfiguration connectionConfiguration) {
    		this.oneToOneMetadataResolver = new OneToOneMetadataResolver(dialect, connectionConfiguration);
    		this.oneToManyMetadataResolver = new OneToManyMetadataResolver(dialect, connectionConfiguration);
    		this.manyToManyMetadataResolver = new ManyToManyMetadataResolver(dialect, connectionConfiguration);
    		this.manyToOneMetadataResolver = new ManyToOneMetadataResolver(dialect, connectionConfiguration);
    		this.mapMetadataResolver = new MapMetadataResolver(dialect, connectionConfiguration);
    		this.elementCollectionMetadataResolver = new ElementCollectionMetadataResolver(dialect);
    	}

    	<C, I> void resolve(EntitySource<C, I> rootEntitySource) {

    		// Non-recursive, stack-based depth-first traversal over the EntitySource tree.
    		// Each element on the stack is an EntitySource to process, processing it produces
    		// new EntitySources (the relation targets) which are pushed onto the stack.
    		Deque<EntitySource<?, ?>> stack = new ArrayDeque<>();
    		stack.push(rootEntitySource);

    		while (!stack.isEmpty()) {
    			EntitySource<?, ?> current = stack.pop();
    			KeepOrderSet<EntitySource<?, ?>> childSources = resolveRelations(current);
    			// Push children so they are processed in subsequent iterations (depth-first)
    			for (EntitySource<?, ?> child : childSources) {
    				stack.push(child);
    			}
    		}
    	}

    	/**
    	 * Resolves all one-to-one and one-to-many relations for the given {@link EntitySource},
    	 * covering both the entity's own configurations and its ancestors'.
    	 *
    	 * @return the set of {@link EntitySource}s produced by the resolved relations, to be enqueued for further traversal
    	 */
    	private <C, I, X> KeepOrderSet<EntitySource<?, ?>> resolveRelations(EntitySource<C, I> source) {

    		KeepOrderSet<EntitySource<?, ?>> childSources = new KeepOrderSet<>();

    		// --- Relations owned by this entity ---
    		childSources.addAll(resolveForConfiguration(source));

    		// --- Relations owned by ancestor entities ---
    		source.<X>getAncestorSources()
    				.forEach(entitySource ->
    						childSources.addAll(resolveForConfiguration(entitySource)));

    		return childSources;
    	}

    	/**
    	 * Resolves all one-to-one and one-to-many relations (including those embedded in insets) and returns the resulting
    	 * children.
    	 */
    	private <C, I> KeepOrderSet<EntitySource<?, ?>> resolveForConfiguration(EntitySource<C, I> source) {

    		KeepOrderSet<EntitySource<?, ?>> newSourcesFound = new KeepOrderSet<>();

    		// One-to-one relations directly on the entity and embedded in insets
    		newSourcesFound.addAll(oneToOneMetadataResolver.resolve(source));
    		// One-to-many relations directly on the entity and embedded in insets
    		newSourcesFound.addAll(oneToManyMetadataResolver.resolve(source));
    		// Many-to-many relations directly on the entity and embedded in insets
    		newSourcesFound.addAll(manyToManyMetadataResolver.resolve(source));
    		// Many-to-many relations directly on the entity and embedded in insets
    		newSourcesFound.addAll(manyToOneMetadataResolver.resolve(source));
    		// Map relations directly on the entity
    		newSourcesFound.addAll(mapMetadataResolver.resolve(source));
    		// Collection of elements' relations directly on the entity and embedded in insets
    		elementCollectionMetadataResolver.resolve(source);

    		return newSourcesFound;
    	}
    }